#include "NodoHoja.h"
#include <algorithm>
using namespace Arboles;

NodoHoja::NodoHoja(unsigned int referenciaNodoSiguiente, unsigned int capacidadMinima, unsigned int capacidadMaxima) :
	Nodo(capacidadMinima, capacidadMaxima) {

	this->referenciaNodoSiguiente = referenciaNodoSiguiente;
	this->setNivel(0);
}

unsigned int NodoHoja::getReferenciaNodoSiguiente() const {
	return referenciaNodoSiguiente;
}

void NodoHoja::setReferenciaNodoSiguiente(unsigned int referenciaNodoSiguiente) {
	this->referenciaNodoSiguiente = referenciaNodoSiguiente;
}


Utils::ResultadoOperacionArbol NodoHoja::actualizarDatoDe(string clave, unsigned int datoViejo, unsigned int datoNuevo) {

	Utils::ResultadoOperacionArbol resultado = Utils::OK;

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
		if (this->getRegistros()->at(i)->getClave() == clave) {

			for (unsigned int j = 0; j < this->getRegistros()->at(i)->getOffsets()->size(); j++) {
				if (this->getRegistros()->at(i)->getOffsets()->at(j) == datoViejo){
					this->getRegistros()->at(i)->getOffsets()->erase(this->getRegistros()->at(i)->getOffsets()->begin() +j);
					this->getRegistros()->at(i)->getOffsets()->push_back(datoNuevo);
					resultado = Utils::ACTUALIZADO;
					break;
				}
			}

			break;
		}
	}

	return resultado;
}

bool sortFunctionRegistrosHoja(RegistroArbol* i, RegistroArbol* j) {
	return (i->getClave() < j->getClave());
}
/* Agrega un SOLO dato en el registro con la clave que nos pasan por parámetro.
 * En el caso de que la clave no exista agrega el registro con ese dato.
 * Retorna el resultado de la operación (por si hubo overflow) */
Utils::ResultadoOperacionArbol NodoHoja::agregar(RegistroArbol* registro) {

	bool existeLaClave = false;

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
		if (this->getRegistros()->at(i)->getClave() == registro->getClave()) {
			this->getRegistros()->at(i)->getOffsets()->push_back(registro->getOffsets()->at(0));
			existeLaClave = true;
			delete registro;
			break;
		}
	}

	if (!existeLaClave) {
		this->getRegistros()->push_back(registro);
		sort(getRegistros()->begin(), getRegistros()->end(), sortFunctionRegistrosHoja);
	}

	if (this->getCapacidadMaxima() < this->getTamanioDelBuffer())
		return Utils::SOBREFLUJO;
	else
		return Utils::ACTUALIZADO;
}

/* Remueve un SOLO dato en el registro con la clave que nos pasan por parámetro.
 * En el caso de que el registro quede sin offsets, se remueve de la lista.
 * Retorna el resultado de la operación (por si hubo underflow) */
Utils::ResultadoOperacionArbol NodoHoja::remover(RegistroArbol* registro) {

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
		if (this->getRegistros()->at(i)->getClave() == registro->getClave()) {

			// elimino el offset
			vector<unsigned int>* offsets = this->getRegistros()->at(i)->getOffsets();
			for (unsigned int j = 0; j < offsets->size(); j++) {
				if (offsets->at(j) == registro->getOffsets()->at(0)) {
					offsets->erase(offsets->begin() + j);
					break;
				}
			}

			// si no hay más offsets en el registro, remuevo el registro
			if (offsets->size() == 0) {
				delete this->getRegistros()->at(i);
				this->getRegistros()->erase(this->getRegistros()->begin() + i);
			}

			break;
		}
	}

	if (this->getCapacidadMinima() >= (this->getTamanioDelBuffer() - 3 * sizeof(int)))
		return Utils::SUBFLUJO;
	else
		return Utils::ACTUALIZADO;
}

void NodoHoja::hidratarse(char* buffer, unsigned int tamanioDelBuffer) {
	//escribo el buffer en el stringStream
	stringstream* ss = new stringstream(stringstream::in | stringstream::out | stringstream::binary);

	ss->seekg(0, ios::beg);
	ss->write(buffer, tamanioDelBuffer);

	//int Nivel
	int nivel = 0;
	ss->read((char*) &(nivel), sizeof(int));
	this->setNivel(nivel);

	//int Referencia a hoja siguiente
	int refHojaSiguiente= 0;
	ss->read((char*) &(refHojaSiguiente), sizeof(int));
	this->referenciaNodoSiguiente = refHojaSiguiente;

	//int Cantidad Registros
	unsigned int cantidadRegistros= 0;
	ss->read((char*) &(cantidadRegistros), sizeof(int));

	//Registro por Registro del nodo
	unsigned int cantidadOffsets= 0;
	unsigned int offset= 0;
	for (unsigned int i = 0; i < cantidadRegistros; i++) {

		RegistroArbol* registro = new RegistroArbol(Utils::ObtenerSiguienteStringVariable(ss));

		ss->read((char*) &(cantidadOffsets), sizeof(int));

		for (unsigned int j = 0; j < cantidadOffsets; j++) {
			ss->read((char*) &(offset), sizeof(int));
			registro->getOffsets()->push_back(offset);
		}

		this->getRegistros()->push_back(registro);

	}

	delete ss;
}

char* NodoHoja::construirBuffer() {

	//agrupo los atributos
	stringstream* ss = new stringstream(stringstream::in | stringstream::out | stringstream::binary);
	ss->seekp(0, ios::end);

	//Nivel
	int nivel = this->getNivel();
	ss->write((char*) &(nivel), sizeof(int));

	//Referencia a Hoja siguiente this->getReferenciaHojaSiguiente()
	ss->write((char*) &(this->referenciaNodoSiguiente), sizeof(int));

	//Cantidad de Registros
	unsigned int cantidadDeRegistros = this->getRegistros()->size();
	ss->write((char*) &cantidadDeRegistros, sizeof(int));

	//Registro por Registro del nodo
	unsigned int cantidadOffsets;
	unsigned int offset;
	for (unsigned int i = 0; i < cantidadDeRegistros; i++) {

		//Tamanio y Clave del Registro
		string claveRegistro = this->getRegistros()->at(i)->getClave();
		unsigned int tamanioClave = claveRegistro.size();
		ss->write((char*) &tamanioClave, sizeof(int));
		ss->write(claveRegistro.c_str(), tamanioClave);

		//Offsets del Registro
		cantidadOffsets = this->getRegistros()->at(i)->getOffsets()->size();
		ss->write((char*) &cantidadOffsets, sizeof(int));
		for (unsigned int j = 0; j < cantidadOffsets; j++) {
			offset = this->getRegistros()->at(i)->getOffsets()->at(j);
			ss->write((char*) &offset, sizeof(int));
		}
	}

	//construyo el buffer
	unsigned int tamanioDelBuffer = this->getTamanioDelBuffer();
	char* buffer = new char[tamanioDelBuffer];
	ss->read(buffer, tamanioDelBuffer);
	delete ss;

	//lo devuelvo
	return buffer;
}

unsigned int NodoHoja::getTamanioDelBuffer() {

	// Nivel + Referencia Hoja Siguiente + Cant. Registros
	int tamanioDelBuffer = 3 * sizeof(int);

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
		tamanioDelBuffer += 2 * sizeof(int); // Tamanio Clave Registro + Cantidad de offsets
		tamanioDelBuffer += this->getRegistros()->at(i)->getClave().size(); //Longitud de la clave del registro
		unsigned int cantOffsets = this->getRegistros()->at(i)->getOffsets()->size();
		tamanioDelBuffer += cantOffsets * sizeof(int); //Cantidad de offsets por el tamanio de cada uno
	}

	return tamanioDelBuffer;
}

NodoHoja::~NodoHoja() {

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
		delete this->getRegistros()->at(i);
	}

	delete this->getRegistros();
}
